﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using Ini;

namespace SpriteToClient
{
    class Program
    {
        struct Item {
			public string spriteName, description;
            public int id;
            public string name_e;
            public string name_j;
            public int type;
            public int price_buy, price_sell;
            public int weight;
            public string atk_matk;
            public int defence;
            public int range;
            public int slots;
            public long equip_jobs;
            public int equip_upper;
            public int equip_genders;
            public int equip_locations;
            public int weapon_level;
            public int equip_level;
            public int refineable;
            public int view;
            public string script, equip_script, unequip_script;
        }

		struct Config
		{
			public string SprConviewPath;
			public int usePreview;
			public int useScript;
			public Item defaultItem;
		}

		[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
		public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);


        static List<string> files = new List<string>();
        static List<Item> items = new List<Item>();
        static string outputFile = "";
		static int view = 0;
		static Config config;

        static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                if (ReadFiles(args[0])) // files
                {
                    Console.WriteLine("Reading files of directory \'" + args[0] + "\'\r\n");
                }
				else if (File.Exists(args[0]))
				{
					files.AddRange(args);
				}
				else
				{
					Console.WriteLine(String.Join("\r\n", args) + "Error: Error parsing arguments");
					Console.ReadKey(true);
					return;
				}
				ReadConfigFile();
                //Console.WriteLine(String.Join("\r\n", files));
				foreach (string f in files)
				{
					MakeItem(f);
					WriteAccId();
					WriteAccName();
					WriteNum2Desc();
					WriteNum2Name();
					WriteNum2Res();
					WriteSlotCount();
					WriteSQL();
					File.WriteAllText("C:\\output.txt", outputFile);
				}
                Console.WriteLine("Finished!");
                Console.WriteLine(outputFile);
            }
            else Console.WriteLine("Error: No Parameter specified");

            Console.ReadKey(true);
        }

		static void ReadConfigFile()
		{
			IniFile conf = new IniFile(AppDomain.CurrentDomain.BaseDirectory + "config.ini");
			//use Preview?
			int.TryParse(conf.IniReadValue("Config", "UsePreview"), out config.usePreview);
			if(config.usePreview == 1)
				config.SprConviewPath = conf.IniReadValue("Config", "SprConviewPath"); //Set SprConview Path
			int.TryParse(conf.IniReadValue("Config", "UseScript"), out config.useScript); //Use Script Edits?

			//Default item
			int.TryParse(conf.IniReadValue("Item", "Type"), out config.defaultItem.type);
			int.TryParse(conf.IniReadValue("Item", "Buy"), out config.defaultItem.price_buy);
			int.TryParse(conf.IniReadValue("Item", "Sell"), out config.defaultItem.price_sell);
			int.TryParse(conf.IniReadValue("Item", "Weight"), out config.defaultItem.weight);
			config.defaultItem.atk_matk= conf.IniReadValue("Item", "AtkMatk");
			int.TryParse(conf.IniReadValue("Item", "Defence"), out config.defaultItem.defence);
			int.TryParse(conf.IniReadValue("Item", "Range"), out config.defaultItem.range);
			int.TryParse(conf.IniReadValue("Item", "Slots"), out config.defaultItem.slots);
			if(!long.TryParse(conf.IniReadValue("Item", "Jobs"), out config.defaultItem.equip_jobs))
				long.TryParse(conf.IniReadValue("Item", "Jobs"), System.Globalization.NumberStyles.HexNumber, null, out config.defaultItem.equip_jobs);
			int.TryParse(conf.IniReadValue("Item", "Upper"), out config.defaultItem.equip_upper);
			int.TryParse(conf.IniReadValue("Item", "Genders"), out config.defaultItem.equip_genders);
			int.TryParse(conf.IniReadValue("Item", "WeaponLevel"), out config.defaultItem.weapon_level);
			int.TryParse(conf.IniReadValue("Item", "EquipLevel"), out config.defaultItem.equip_level);
			int.TryParse(conf.IniReadValue("Item", "Refineable"), out config.defaultItem.refineable);
			int.TryParse(conf.IniReadValue("Item", "View"), out config.defaultItem.view);
		}

        static bool ReadFiles(string dir)
        {
            if(Directory.Exists(dir))
            {
                files.AddRange(Directory.GetFiles(dir));
                return true;
            }
            return false;
        }

		static void MakeItem(string str)
		{
			if (str.EndsWith(".spr"))
			{
				Item item = new Item();
				item.spriteName = str.Substring(str.LastIndexOf('\\') + 1, str.Length - str.LastIndexOf('\\') - 5);
				//Open SprConview
				Process p = null;
				if (config.usePreview == 1)
				{
					ProcessStartInfo pi = new ProcessStartInfo(config.SprConviewPath, str);
					pi.CreateNoWindow = true;
					p = Process.Start(pi);
				}
				Console.WriteLine("Item [" + item.spriteName + "]:");

				//ID
				do
					Console.Write("ID: ");
				while (!int.TryParse(Console.ReadLine(), out item.id));
				//Console.WriteLine();


				//Real Name
				Console.Write("Name (with spaces, no _): ");
				item.name_j = Console.ReadLine();
				item.name_e = item.name_j.Replace(' ', '_');
				//Console.WriteLine();

				//Description
				Console.Write("Description (use $ for break): ");
				item.description = Console.ReadLine().Replace("$", "\r\n");
				//Console.WriteLine();

				//Type
				Console.Write("Type (4 = Weapon, 5 = Armor, 6 = Card, ...)" + ((config.defaultItem.type != 0) ? "(default " + config.defaultItem.type + ")" : "") + ": ");
				if(!int.TryParse(Console.ReadLine(), out item.type)) item.type = config.defaultItem.type;
				//Console.WriteLine();

				//Buy Price
				Console.Write("Buy Price: " + ((config.defaultItem.price_buy != 0) ? "(default " + config.defaultItem.price_buy + ")" : "") + "");
				if(!int.TryParse(Console.ReadLine(), out item.price_buy)) item.price_buy = config.defaultItem.price_buy;
				//Console.WriteLine();


				//Sell Price
				Console.Write("Sell Price: " + ((config.defaultItem.price_sell != 0) ? "(default " + config.defaultItem.price_sell + ")" : "") + "");
				if(!int.TryParse(Console.ReadLine(), out item.price_sell)) item.price_sell = config.defaultItem.price_sell;
				//Console.WriteLine(); 

				//Weight
				Console.Write("Weight*10 (1 = 0.1): " + ((config.defaultItem.weight != 0) ? "(default " + config.defaultItem.weight + ")" : "") + "");
				if(!int.TryParse(Console.ReadLine(), out item.weight)) item.weight = config.defaultItem.weight;
				//Console.WriteLine();

				//Atk:Matk if Weapon
				if (item.type == 4)
				{
					Console.Write("Atk:Matk: " + ((config.defaultItem.atk_matk != "") ? "(default " + config.defaultItem.atk_matk+ ")" : "") + "");
					if((item.atk_matk = Console.ReadLine()) == "") item.atk_matk = config.defaultItem.atk_matk;
					//Console.WriteLine();
				}

				//Defence if armor
				if (item.type == 5)
				{
					Console.Write("Defence: " + ((config.defaultItem.defence != 0) ? "(default " + config.defaultItem.defence + ")" : "") + "");
					if(!int.TryParse(Console.ReadLine(), out item.defence)) item.defence = config.defaultItem.defence;
					//Console.WriteLine();
				}

				//Range if weapon
				if (item.type == 4)
				{
					Console.Write("Range: " + ((config.defaultItem.range != 0) ? "(default " + config.defaultItem.range + ")" : "") + "");
					if(!int.TryParse(Console.ReadLine(), out item.range)) item.range = config.defaultItem.range;
					//Console.WriteLine();
				}

				//Slots if Armor or Weapon
				if (item.type == 4 || item.type == 5)
				{
					Console.Write("Slots: " + ((config.defaultItem.slots != 0) ? "(default " + config.defaultItem.slots + ")" : "") + "");
					if(!int.TryParse(Console.ReadLine(), out item.slots)) item.slots = config.defaultItem.slots;
					//Console.WriteLine();
				}

				//Req Job
				Console.Write("Equip Jobs (HexValue possible): " + ((config.defaultItem.equip_jobs != 0) ? "(default " + config.defaultItem.equip_jobs.ToString("X") + ")" : "") + "");
				string jobs;
				if (!long.TryParse(jobs = Console.ReadLine(), out item.equip_jobs))
					if (!long.TryParse(jobs, System.Globalization.NumberStyles.HexNumber, null, out item.equip_jobs)) item.equip_jobs = config.defaultItem.equip_jobs;
				//Console.WriteLine();

				//Equip Upper
				Console.Write("Equip Upper (2nd Class etc)" + ((config.defaultItem.equip_upper != 0) ? "(default " + config.defaultItem.equip_upper + ")" : "") + ": ");
				if(!int.TryParse(Console.ReadLine(), out item.equip_upper)) item.equip_upper = config.defaultItem.equip_upper;
				//Console.WriteLine();

				//Gender
				Console.Write("Gender " + ((config.defaultItem.equip_genders != 0) ? "(default " + config.defaultItem.equip_genders + ")" : "") + ": ");
				if(!int.TryParse(Console.ReadLine(), out item.equip_genders)) item.equip_genders = config.defaultItem.equip_genders;
				//Console.WriteLine();

				//Equip Location if armor or weapon
				if (item.type == 4 || item.type == 5)
				{
					do
					Console.Write("Equip Locations (1 = Lower, 256 = Upper, 512 = Middle): ");
					while(!int.TryParse(Console.ReadLine(), out item.equip_locations));
					//Console.WriteLine();
				}

				//Weapon level
				if (item.type == 4)
				{
					Console.Write("Weapon Level" + ((config.defaultItem.weapon_level != 0) ? "(default " + config.defaultItem.weapon_level + ")" : "") + ": ");
					if(!int.TryParse(Console.ReadLine(), out item.weapon_level)) item.weapon_level = config.defaultItem.weapon_level;
					//Console.WriteLine();
				}

				//Equio Level
				Console.Write("Eqiup Level (min Lv to equip) " + ((config.defaultItem.equip_level != 0) ? "(default " + config.defaultItem.equip_level + ")" : "") + ": ");
				if(!int.TryParse(Console.ReadLine(), out item.equip_level)) item.equip_level = config.defaultItem.equip_level;
				//Console.WriteLine();

				//Refineable if weapon or armor
				if (item.type == 4 || item.type == 5)
				{
					Console.Write("Refinable " + ((config.defaultItem.refineable != 0) ? "(default " + config.defaultItem.refineable + ")" : "") + ": ");
					if(!int.TryParse(Console.ReadLine(), out item.refineable)) item.refineable = config.defaultItem.refineable;
					//Console.WriteLine();
				}

				//View ID
				view = (view == 0) ? config.defaultItem.view : view;
				Console.Write("View ID (IMPORTANT FOR HEADGEARS)" + ((view != 0) ? "(default " + view + ")" : "") + ": ");
				if (!int.TryParse(Console.ReadLine(), out item.view)) item.view = view++;
				//Console.WriteLine();

				if (config.useScript == 1)
				{
					Console.Write("script (if you want): ");
					item.script = Console.ReadLine();
					//Console.WriteLine();

					Console.Write("equip_script (if you want):");
					item.equip_script = Console.ReadLine();
					//Console.WriteLine();

					Console.Write("unequip_script (if you want):");
					item.unequip_script = Console.ReadLine();
					//Console.WriteLine();
				}

				items.Add(item);

				if (config.usePreview == 1)
					p.Kill();
			}
		}

		#region WriteFiles
		static void WriteAccId()
        {
            string acc_id = "";
			outputFile += "[ACCESSORYID]\r\n\r\n";
            foreach (Item item in items)
            {
				if (item.view > 25)
					acc_id += "ACCESSORY_" + item.spriteName.ToUpper() + "= "+ item.view +",\r\n";
            }
            outputFile += acc_id + "\r\n\r\n\r\n\r\n";
        }
        static void WriteAccName()
        {
            string acc_name = "";
			outputFile += "[ACCNAME]\r\n\r\n";
			foreach (Item item in items)
			{
				if(item.view > 25)
					acc_name += "[ACCESSORY_IDs.ACCESSORY_" + item.spriteName.ToUpper() + "] = \"_" + item.spriteName + "\",\r\n";
			}
            outputFile += acc_name + "\r\n\r\n\r\n\r\n";
        }
        static void WriteNum2Desc()
        {
            string desc = "";
			outputFile += "[NUM2DESC]\r\n\r\n";
            foreach (Item item in items)
            {
				desc += item.id + "#\r\n";																												//Item ID
				desc += item.description + "\r\n";
				if (Type(item.type) != "")	desc += "Class :^777777 " + ((Type(item.type) == "Armor") ? Class(item.equip_locations) : Type(item.type)) + "^000000\r\n";												//Item  Class
				if (item.type == 4)			desc += "Attack :^777777 "+item.atk_matk.Split(':')[0]+"^000000\r\n";										//Item Attack (not matk)
				if (item.type == 5)			desc += "Defense :^777777 " + item.defence + "^000000\r\n";													//Item Def
				if (item.type == 6)			desc += "Compound on :^777777 " + EquippedOn(item.equip_locations) + "^000000\r\n";							//Card Compound on
				if (EquippedOn(item.equip_locations).Length != 0) 
											desc += "Equipped on :^777777 " + String.Join(", ", EquippedOn(item.equip_locations)) + "^000000\r\n";		//Headgear Location
											desc += "Weight :^777777 " + item.weight/10 + "^000000\r\n";												//Weight
				//							desc += "Property :" + "COLOR" + " " + "PROP" + "^000000\r\n";												//Property TODO
				if (item.weapon_level != 0) desc += "Weapon Level :^777777 "+item.weapon_level+"^000000\r\n";											//Weapon Level
				if (item.equip_level != 0)	desc += "Required Level :^777777 "+item.equip_level+"^000000\r\n";											//Req Level
				if (item.type == 4 ||
					item.type == 5) desc += "Applicable Job :^777777 " + String.Join(", ", Job(item.equip_jobs)) + "^000000\r\n#\r\n\r\n";				//Req Job
            }
            outputFile += desc + "\r\n\r\n\r\n\r\n";
        }
        static void WriteNum2Name()
        {
            string name = "";
			outputFile += "[NUM2NAME]\r\n\r\n";
            foreach (Item item in items)
                name += item.id +"#" + item.name_e + "#\r\n";
            outputFile += name + "\r\n\r\n\r\n\r\n";
        }
        static void WriteNum2Res()
        {
            string res = "";
			outputFile += "[NUM2RES]\r\n\r\n";
			foreach (Item item in items)
                res += item.id + "#" + item.spriteName + "#\r\n";
            outputFile += res + "\r\n\r\n\r\n\r\n";
        }
        static void WriteSlotCount()
        {
            string slot = "";
			outputFile += "[SLOTCOUNT]\r\n\r\n";
			foreach (Item item in items)
                slot += item.id + "#"+item.slots+"#\r\n";
            outputFile += slot + "\r\n\r\n\r\n\r\n";
        }
        static void WriteSQL()
        {
            string  sql = "";
			outputFile += "[MYSQL]\r\n\r\n";
			foreach (Item item in items)
				sql += "INSERT INTO item_db2 (" +
					"`id`, `name_english`, `name_japanese`, `type`, `price_buy`, `price_sell`, `weight`, `atk:matk`, `defence`, `range`, `slots`, `equip_jobs`, `equip_upper`, `equip_genders`," +
					"`equip_locations`, `weapon_level`, `equip_level`, `refineable`, `view`, `script`, `equip_script`, `unequip_script`) " +
					"VALUES (" + item.id + ", \"" + item.name_e + "\", \"" + item.name_j + "\", " + item.type + ", " + item.price_buy + ", " + item.price_sell + ", " + item.weight + ", \"" + 
					item.atk_matk + "\", " +item.defence + ", " + item.range + ", " + item.slots + ", " + item.equip_jobs + ", " + item.equip_upper + ", " + item.equip_genders + ", " + item.equip_locations + ", " +
					item.weapon_level + ", " + item.equip_level + ", " + item.refineable + ", " + item.view + ", \"" + item.script + "\", \"" + item.equip_script + "\", \"" + item.unequip_script + "\");\r\n";
            outputFile += sql + "\r\n\r\n\r\n\r\n";
        }
		#endregion
		
		#region Parser
		static string Type(int typeid)
		{
			switch (typeid)
			{
				case 4:
					return "Weapon";
				case 5:
					return "Armor";
				case 6:
					return "Card";
				case 8:
					return "Cute Pet Armor";
				case 10:
					return "Ammu";
				default:
					return "";
			}
		}

        static string Class(int bitmask)
        {
			string cl = "";
            if ( (bitmask & 1) != 0		|| 
				 (bitmask & 256) != 0	|| 
				 (bitmask & 512) != 0	)	cl = "Headgear";

            if ( (bitmask & 2) != 0 ||
				 (bitmask & 32) != 0)		cl = "Weapon";

            if ( (bitmask & 4) != 0)		cl = "Garment";

            if ( (bitmask & 8) != 0 ||
				 (bitmask & 128) != 0)		cl = "Accessory";

            if ((bitmask & 16) != 0)		cl = "Armor";

            if ((bitmask & 64) != 0)		cl = "Shoes";

			//if ((bitmask & 1024) != 0)		cl = "";
			//if ((bitmask & 2048) != 0)		cl = "";
			//if ((bitmask & 4096) != 0)		cl = "";

            if ((bitmask & 32768) != 0)		cl = "Arrow";


			return cl;
        }

        static string[] EquippedOn(int bitmask)
        {
            List<string> eq = new List<string>();
            if ((bitmask & 1) != 0)     eq.Add("Lower");
            //if ((bitmask & 2) != 0)     eq.Add("");
            //if ((bitmask & 4) != 0)     eq.Add("");
            //if ((bitmask & 8) != 0)     eq.Add("");
            //if ((bitmask & 16) != 0)    eq.Add("");
            //if ((bitmask & 32) != 0)    eq.Add("");
            //if ((bitmask & 64) != 0)    eq.Add("");
            //if ((bitmask & 128) != 0)   eq.Add("");
            if ((bitmask & 256) != 0)   eq.Add("Upper");
            if ((bitmask & 512) != 0)   eq.Add("Mid");
            //if ((bitmask & 1024) != 0)  eq.Add("");
            //if ((bitmask & 2048) != 0)  eq.Add("");
            //if ((bitmask & 4096) != 0)  eq.Add("");
            //if ((bitmask & 32768) != 0) eq.Add("");
            return eq.ToArray();
        }

		static string[] Job(long bitmask)
		{
			List<string> job = new List<string>();

			if ((bitmask & 0xFFFFFFFF) != 0) return new string[] { "Every Job" };
			if((bitmask & 0xFFFFFFFE) != 0)		return new string[] {"Every Job Except Novice"};

			if ((bitmask & 0x00000001) != 0)	job.Add("Novice");
			if ((bitmask & 0x00000002) != 0)	job.Add("Swordman");
			if ((bitmask & 0x00000004) != 0)	job.Add("Mage");
			if ((bitmask & 0x00000008) != 0)	job.Add("Archer");
			if ((bitmask & 0x00000010) != 0)	job.Add("Acolyte");
			if ((bitmask & 0x00000020) != 0)	job.Add("Merchant");
			if ((bitmask & 0x00000040) != 0)	job.Add("Thief");
			if ((bitmask & 0x00000080) != 0)	job.Add("Knight");
			if ((bitmask & 0x00000100) != 0)	job.Add("Priest");
			if ((bitmask & 0x00000200) != 0)	job.Add("Wizard");
			if ((bitmask & 0x00000400) != 0)	job.Add("Blacksmith");
			if ((bitmask & 0x00000800) != 0)	job.Add("Hunter");
			if ((bitmask & 0x00001000) != 0)	job.Add("Assassin");
			//if ((bitmask & 0x00002000) != 0)	job.Add("");
			if ((bitmask & 0x00004000) != 0)	job.Add("Crusader");
			if ((bitmask & 0x00008000) != 0)	job.Add("Monk");
			if ((bitmask & 0x00010000) != 0)	job.Add("Sage");
			if ((bitmask & 0x00020000) != 0)	job.Add("Rogue");
			if ((bitmask & 0x00040000) != 0)	job.Add("Alchemist");
			if ((bitmask & 0x00080000) != 0)	job.Add("Bard/Dancer");
			//if ((bitmask & 0x00100000) != 0)	job.Add("");
			if ((bitmask & 0x00200000) != 0)	job.Add("Taekwon");
			if ((bitmask & 0x00400000) != 0)	job.Add("Star Gladiator");
			if ((bitmask & 0x00800000) != 0)	job.Add("Soul Linker");
			if ((bitmask & 0x01000000) != 0)	job.Add("Gunslinger");
			if ((bitmask & 0x02000000) != 0)	job.Add("Ninja");

			return job.ToArray();
		}
		#endregion
	}
}
